package n50_atomic;

import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * @author fu
 * @date 2022-02-13 17:57
 * 概要：
 * ----------------------------------------------------------------
 * |                                                              |
 * |              原子数组 保护 数组里面的元素                       |
 * |                                                             |
 * |==============================================================
 *
 * AtomicIntegerArray
 * AtomicLongArray
 * AtomicReferenceArray
 *
 */
@Slf4j(topic = "c.T7")
public class T7_原子数组_包含函数式编程知识 {

    public static void main(String[] args) {
        demo(
                ()             -> new int[10],
                (array)        -> array.length,
                (array, index) -> array[index]++,
                array          -> System.out.println(Arrays.toString(array))
        );


        demo(
                ()             -> new AtomicIntegerArray(10),
                (array)        -> array.length(),
                (array, index) -> array.getAndIncrement(index),
                array          -> System.out.println(array)
        );

    }

    /**
     参数1，提供数组、可以是线程不安全数组或线程安全数组
     参数2，获取数组长度的方法
     参数3，自增方法，回传 array, index
     参数4，打印数组的方法
     */
// supplier 提供者   无中生有 ()->结果
// function 函数     一个参数一个结果 (参数)->结果 ,   BiFunction (参数1,参数2)->结果
// consumer 消费者   一个参数没结果 (参数)->void,     BiConsumer (参数1,参数2)-void
    private static <T> void demo(
            Supplier<T> arraySupplier,
            Function<T, Integer> lengthFun,
            BiConsumer<T, Integer> putConsumer,
            Consumer<T> printConsumer ) {

        List<Thread> ts = new ArrayList<>();
        T array = arraySupplier.get();
        int length = lengthFun.apply(array);                 // 利用apply 来调用 第二参数 方法

        for (int i = 0; i < length; i++) {
            // 每个线程对数组作 10000 次操作
            ts.add(new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
//                    final int ll = j % length;
//                    log.debug("{}%{} : {}",j,length,ll);
                    putConsumer.accept(array, j%length);  // 利用accept 来调用 第三参数 方法
                }
            }));
        }
        ts.forEach(t -> t.start()); // 启动所有线程
        ts.forEach(t -> {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }); // 等所有线程结束
        printConsumer.accept(array);                          // 利用accept 来调用 第四参数 方法
    }

}
